#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <pthread.h>
#include "vectors.h"
#include "raytrace.h"
#include "/usr/include/SDL/SDL.h"
#include "/usr/include/SDL/SDL_image.h"
#include "fileformat.h"
#include "scene.h"
#define PI 3.14159265
#define TRUE 1
#define FALSE 0
#define NUMTHREADS 12

scene myScene;
unsigned char *img = NULL;



bool collideRaySphere(ray *r, sphere *s, float *t){
 
	/* Function return value */
	bool retvalue = FALSE;
	
	/* Compute A, B and C coefficients */
	float a = VectorDot(&r->dir, &r->dir);
	vector dist = VectorSub(&r->start, &s->pos);
	float b = 2 * VectorDot(&dist, &r->dir);
	float c = VectorDot(&dist, &dist) - (s->r * s->r);
 
	/* Find discriminant */
	float disc = b * b - 4 * a * c;

	/* If discriminant is negative, there are no real roots.
	 * Return false as the ray misses the sphere. */
	if(disc < 0)
		return FALSE;
	
	/* Compute q */
	float discSqrt = sqrtf(disc);
	float q;
	
	if(b < 0)
		q = (-b - discSqrt)/2.0;
	else
		q = (-b + discSqrt)/2.0;
	
	/* The roots */
	float t0 = q / a;
	float t1 = c / q;
	
	if(t0 > t1)
		t0 = t1;
	
	/* Verify t0 larger than 0 and less than the original t */
	if((t0 > 0.001f) && (t0 < *t)){
		*t = t0;
		retvalue = TRUE;
	}
	
	return retvalue;
 }
void renderThread(int t){

	//printf("Render function start yay! %d \n", t);


	int sectionsize = HEIGHT/NUMTHREADS;
	int limits[] = {(t*sectionsize), (t*sectionsize)+sectionsize};

	int x,y;
	//printf("Thread: %d Limit1: %d Limit2: %d \n", t, limits[0], limits[1]);
	for (y = limits[0]; y < limits[1]; ++y){
		for (x = 0; x < myScene.width; ++x) {
			
			float red = 0, green = 0, blue = 0;
			float coef = 1.0f;
			int level = 0;
			// Cast the ray 
			ray viewRay = { {(float)x, (float)y, -10000.0f}, { 0.0f, 0.0f, 1.0f}};
			do{
				// Find closest intersection 
				float t = 20000.0f;
				int currentSphere= -1;
				
				unsigned int i;
				for(i = 0; i < SPHERES; ++i){
					if (collideRaySphere(&viewRay, &myScene.spheres[i], &t)){
						currentSphere = i;
					}
				}
				if (currentSphere == -1) break;
				
				vector scaled = VectorScale(t, &viewRay.dir);
				vector newStart = VectorAdd(&viewRay.start, &scaled);
				
				// Find the normal for this new vector at the point of intersection
				vector n = VectorSub(&newStart, &myScene.spheres[currentSphere].pos);
				float temp = VectorDot(&n,&n);
				if (temp == 0.0f) break;
				
				temp = 1.0f / sqrtf(temp);
				n = VectorScale(temp, &n);
				
				// Get current material to determine the colour 
				material currentMat = myScene.materials[myScene.spheres[currentSphere].mat];
				
				// Find the value of the light at this point 
				unsigned int j;
				for (j = 0; j < LIGHTS; ++j){
					light current = myScene.lights[j];
					vector dist = VectorSub(&current.pos,&newStart);
					if (VectorDot(&n,&dist) <= 0.0f) continue;
					float t = sqrtf(VectorDot(&dist,&dist));
					if (t <= 0.0f) continue;
					ray lightRay;
					lightRay.start = newStart;
					lightRay.dir = VectorScale((1/t), &dist); 
					
					// Calculate the shadows 
					bool inShadow = FALSE;
					unsigned int k;
					for (k = 0; k < SPHERES; ++k) {
						if (collideRaySphere(&lightRay, &myScene.spheres[k], &t)){
							inShadow = TRUE;
							break;
						}
					}
					if (!inShadow){
						//Diffuse reflaction using Lambert
						float lambert = VectorDot(&lightRay.dir, &n) * coef;
						red += lambert * current.intensity.red * currentMat.diffuse.red;
						green += lambert * current.intensity.green * currentMat.diffuse.green;
						blue += lambert * current.intensity.blue * currentMat.diffuse.blue;
					}         
 
				}
				// Iterate over the reflection 
				coef *= currentMat.ref;
				float reflect = 2.0f * VectorDot(&viewRay.dir, &n);
				viewRay.start = newStart;
				vector tmp = VectorScale(reflect, &n);
				viewRay.dir = VectorSub(&viewRay.dir, &tmp);
				level++;
				// Limit iteration depth to 10        
			}
			while ((coef > 0.0f) && (level < 10));;
			img[(x+y*myScene.width)*3+2] = (unsigned char)min(red*255.0f, 255.0f);
			img[(x+y*myScene.width)*3+1] = (unsigned char)min(green*255.0f, 255.0f);
			img[(x+y*myScene.width)*3+0] = (unsigned char)min(blue*255.0f,255.0f);	   

		}
	}
}


 
int main(int argc, char *argv[]){
bool quit = FALSE; 

SDL_Event event;
SDL_Surface* image = NULL; 
SDL_Surface* screen = NULL;


    
    sphere move;
	move.pos.x = 300;
	move.pos.y = 300;
	move.pos.z = 300;
	move.r = 80;
	move.mat = 3;


void cleanUp(){
	SDL_FreeSurface(screen);
	SDL_Quit();
}


bool renderImage(){
	unsigned char *ret_img = NULL;
	int t = 0;
	SDL_RWops *rw;
	pthread_attr_t tattr;
	pthread_t tid[NUMTHREADS];
	int ret[NUMTHREADS];
	
	construct(&myScene, &move);

	if(img != NULL){ free(img); }
	img = (unsigned char *)malloc(3*WIDTH*HEIGHT);
	memset(img,0,sizeof(img));

	for(t = 0;t < NUMTHREADS; t++){
		//printf("Creating thread %d! \n", t);
		ret[t] = pthread_create(&tid[t], NULL, renderThread, (void *)t);
	}
	//if(success) savebmp("./out.bmp", img);

	for(t = 0; t < NUMTHREADS; t++){
		//printf("Waiting for thread %d to join. \n", t);
		ret[t] = pthread_join(tid[t], NULL);

	}
	
	ret_img = savebmp_mem(img);
	if(ret_img == NULL) { return FALSE; }
	rw = SDL_RWFromMem(ret_img, 54+(3*WIDTH*HEIGHT));
	image = SDL_LoadBMP_RW(rw, 0);
return TRUE;
}


bool draw(){
	bool success;
	success = renderImage();
	if(success == FALSE){ return FALSE; } 
	//Apply the image to the screen 
	SDL_BlitSurface( image, NULL, screen, NULL );
	SDL_FreeSurface(image);
	SDL_Flip(screen);
return TRUE;
}

bool init(){
	if(SDL_Init( SDL_INIT_EVERYTHING ) == -1){ return FALSE; }
	
	screen = SDL_SetVideoMode( WIDTH, HEIGHT, 32, SDL_SWSURFACE );
	
	if(screen == NULL){ return FALSE; }
	
	SDL_WM_SetCaption( "Raytracer, now with more SDL!", NULL );	
	
return TRUE;	
}
//Function definitons end here



if(init() == FALSE){
	printf("Init error! \n");
	return -1;	
}
if(draw()== FALSE){
	printf("Draw error! \n");
	return -1;
}
  

if( SDL_Flip( screen ) == -1 ){ 
	return 1;
} 



/*
while( quit == FALSE && SDL_WaitEvent( &event ) ) { 
	if( event.type == SDL_KEYDOWN ){
		switch( event.key.keysym.sym ){  
			case SDLK_ESCAPE: quit = TRUE; break;
			case SDLK_UP: move.pos.y += 5; draw(); ; break;
			case SDLK_DOWN: move.pos.y -= 5; draw(); break;
			case SDLK_LEFT: move.pos.x -= 5; draw(); break;
			case SDLK_RIGHT: move.pos.x += 5; draw(); break;
			case SDLK_q: move.pos.z -= 40; draw(); break;
			case SDLK_w: move.pos.z += 40; draw(); break;
			case SDLK_a: move.r += 10; draw(); break;
			case SDLK_s: move.r -= 10; draw(); break;
			default : ;
		}
	}
	if( event.type == SDL_QUIT ) { 
		quit = TRUE; 
	}	
} */

   while( quit == FALSE ){
        while( SDL_PollEvent( &event ) ){
            if( event.type == SDL_QUIT ){
                quit = TRUE;
            }
        }


        Uint8 *keystates = SDL_GetKeyState( NULL );

        if( keystates[ SDLK_UP ] && keystates[ SDLK_LEFT ] ){
            move.pos.y += 5; move.pos.x -= 5; draw();
        }
		if( keystates[ SDLK_DOWN ] && keystates[ SDLK_RIGHT ] ){
            move.pos.y -= 5; move.pos.x += 5; draw();
        }


        if( keystates[ SDLK_UP ] && keystates[ SDLK_RIGHT ] ){
            move.pos.y += 5; move.pos.x += 5; draw();
        }else{
        	if( keystates[ SDLK_UP ] ){
				move.pos.y += 5; draw();
			}
			if( keystates[ SDLK_RIGHT ] ){
				move.pos.x += 5; draw();
			}

		}
		
        if( keystates[ SDLK_DOWN ] && keystates[ SDLK_LEFT ] ){
            move.pos.y -= 5; move.pos.x -= 5; draw();
        }else{
        	if( keystates[ SDLK_DOWN ] ){
				move.pos.y -= 5; draw();
			}
			if( keystates[ SDLK_LEFT ] ){
				move.pos.x -= 5; draw();
			}

		}

        if( keystates[ SDLK_a ] ){
            move.r += 2; draw();
        }

        if( keystates[ SDLK_s ] ){
            move.r -= 2; draw();
        }

        if( keystates[ SDLK_q ] ){
            move.pos.z -= 10; draw();
        }
        if( keystates[ SDLK_w ] ){
            move.pos.z += 10; draw();
        }
        if( keystates[ SDLK_ESCAPE ] ){
            quit = TRUE;
        }
    }




/* I've included two sets of key handlers, the other one is commented above. You can try them both out.
 * The difference is that the one that is currently commented out uses WaitEvent instead of PollEvent
 * which gets us rid of that pesky processor usage problem with PollEvent. And keystates don't work with
 * WaitEvent.
 * I don't know if it has any role in determining the speed at which the render part completes though.
 */

cleanUp();

return 0;
}




